#include "fileexchange.h"

bool exchangeFile( std::string local_file, std::string remote_file, std::string server_addr )
{
    int ret;
    char msg_type;
    struct sockaddr_in addr_srvr;  // AF_INET
    unsigned int len_inet;         // tamanho
    int sock;                      // socket
    char *dgram;                   // buffer
    char size_msg[5];              // msg with the size of INFO_MSG
    int info_size;                 // size of INFO_MSG

    std::cout << "Iniciando troca de arquivos" << std::endl;

    memset( &addr_srvr , 0 , sizeof( addr_srvr ));
    addr_srvr.sin_family = AF_INET;
    addr_srvr.sin_port = htons( 9000 );
    addr_srvr.sin_addr.s_addr = inet_addr( server_addr.c_str() );
    if( addr_srvr.sin_addr.s_addr == INADDR_NONE )
    {
        std::cout << "Erro no endereco!" << std::endl;
        return false;
    }

    len_inet = sizeof( addr_srvr );

    // Criando o socket UDP
    sock = socket( AF_INET , SOCK_DGRAM , 0 );
    if ( sock == -1 )
    {
        std::cout << "Erro no socket" << std::endl;
        return false;
    }

    // Mandar HELLO_MSG
    std::cout << "Enviando HELLO_MSG" << std::endl;
    msg_type = HELLO_MSG;
    ret = sendto( sock, &msg_type, sizeof(char) , 0 , (struct sockaddr *)&addr_srvr , len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no sendto!" << std::endl;
        return false;
    }

    // Receber ACK_MSG
    std::cout << "Esperando ACK_MSG" << std::endl;
    ret = recvfrom( sock, &msg_type, sizeof(char), 0, (struct sockaddr *)&addr_srvr , &len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no recvfrom!" << std::endl;
        return false;
    }
    if( msg_type != ACK_MSG )
    {
        std::cout << "Erro no recebimento de ACK_MSG!" << std::endl;
        return false;
    }

    // Ler do arquivo para vetor de caracteres &dgram[1]
    std::ifstream rfile( local_file.c_str(), std::ios::in|std::ios::binary|std::ios::ate );
    if( rfile.is_open() )
    {
        int size = (int) rfile.tellg();
        info_size = size + 1;
        dgram = new char [info_size];
        rfile.seekg( 0, std::ios::beg );
        rfile.read( &dgram[1], size );
        rfile.close();

        std::cout << "Conteudo do arquivo local copiado para a memoria" << std::endl;
    }
    else
    {
        std::cout << "Erro ao ler arquivo" << std::endl;
        return false;
    }
    dgram[0] = INFO_MSG;

    // Mandar SIZE_MSG
    std::cout << "Enviando SIZE_MSG" << std::endl;
    size_msg[0] = SIZE_MSG;
    memcpy( &size_msg[1], &info_size, sizeof(int) );
    ret = sendto( sock, size_msg, sizeof(size_msg) , 0 , (struct sockaddr *)&addr_srvr , len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no sendto!" << std::endl;
        return false;
    }

    // Receber ACK_MSG
    std::cout << "Esperando ACK_MSG" << std::endl;
    ret = recvfrom( sock, &msg_type, sizeof(char), 0, (struct sockaddr *)&addr_srvr , &len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no recvfrom!" << std::endl;
        return false;
    }
    if( msg_type != ACK_MSG )
    {
        std::cout << "Erro no recebimento de ACK_MSG!" << std::endl;
        return false;
    }

    // Mandar INFO_MSG
    std::cout << "Enviando INFO_MSG" << std::endl;
    ret = sendto( sock, dgram, info_size , 0 , (struct sockaddr *)&addr_srvr , len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no sendto!" << std::endl;
        return false;
    }

    delete [] dgram;

    // Receber SIZE_MSG
    std::cout << "Esperando SIZE_MSG" << std::endl;
    ret = recvfrom( sock, size_msg, sizeof(size_msg), 0, (struct sockaddr *)&addr_srvr , &len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no recvfrom!" << std::endl;
        return false;
    }
    if( size_msg[0] != SIZE_MSG )
    {
        std::cout << "Erro no recebimento de SIZE_MSG!" << std::endl;
        return false;
    }
    memcpy( &info_size, &size_msg[1], sizeof(int) );

    // Alocar dgram com tamanho info_size;
    dgram = new char [info_size];

    // Mandar ACK_MSG
    std::cout << "Enviando ACK_MSG" << std::endl;
    msg_type = ACK_MSG;
    ret = sendto( sock, &msg_type, sizeof(char) , 0 , (struct sockaddr *)&addr_srvr , len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no sendto!" << std::endl;
        return false;
    }

    // Receber INFO_MSG
    std::cout << "Esperando INFO_MSG" << std::endl;
    ret = recvfrom( sock, dgram, info_size, 0, (struct sockaddr *)&addr_srvr , &len_inet );
    if( ret < 0 )
    {
        std::cout << "Erro no recvfrom!" << std::endl;
        return false;
    }
    if( dgram[0] != INFO_MSG )
    {
        std::cout << "Erro no recebimento de INFO_MSG!" << std::endl;
        return false;
    }

    // Escrever INFO_MSG em arquivo
    std::ofstream wfile( remote_file.c_str(), std::ios::out|std::ios::binary|std::ios::trunc );
    if( wfile.is_open() )
    {
        int size = info_size-1;
        wfile.write( &dgram[1], size );
        wfile.close();

        std::cout << "Conteudo do pacote escrito em arquivo" << std::endl;
    }
    else
    {
        std::cout << "Erro ao escrever arquivo" << std::endl;
        return false;
    }

    delete [] dgram;

    close(sock);

    return true;
}
